import type {Recordable} from '@/typings/core';
import type {
    FormState,
    GenericObject,
    ResetFormOpts,
    ValidationOptions,
} from 'vee-validate';

import type {FormActions, FormSchema, JcxFormProps} from './types';

import {toRaw} from 'vue';

import {Store} from '@/utils/shared/store';
import {
    bindMethods,
    createMerge,
    isDate,
    isDayjsObject,
    isFunction,
    isObject,
    mergeWithArrayOverride,
    StateHandler,
} from '@/utils/shared/utils';

function getDefaultState(): JcxFormProps {
    return {
        actionWrapperClass: '',
        collapsed: false,
        collapsedRows: 1,
        collapseTriggerResize: false,
        commonConfig: {},
        handleReset: undefined,
        handleSubmit: undefined,
        handleValuesChange: undefined,
        layout: 'horizontal',
        resetButtonOptions: {},
        schema: [],
        showCollapseButton: false,
        showDefaultActions: true,
        submitButtonOptions: {},
        submitOnChange: false,
        submitOnEnter: false,
        wrapperClass: 'grid-cols-1',
    };
}

export class FormApi {
    // 最后一次点击提交时的表单值
    private latestSubmissionValues: null | Recordable<any> = null;
    private prevState: null | JcxFormProps = null;

    // private api: Pick<JcxFormProps, 'handleReset' | 'handleSubmit'>;
    public form = {} as FormActions;
    isMounted = false;

    public state: null | JcxFormProps = null;

    stateHandler: StateHandler;

    public store: Store<JcxFormProps>;

    constructor(options: JcxFormProps = {}) {
        const {...storeState} = options;

        const defaultState = getDefaultState();

        this.store = new Store<JcxFormProps>(
            {
                ...defaultState,
                ...storeState,
            },
            {
                onUpdate: () => {
                    this.prevState = this.state;
                    this.state = this.store.state;
                    this.updateState();
                },
            },
        );

        this.state = this.store.state;
        this.stateHandler = new StateHandler();
        bindMethods(this);
    }

    private async getForm() {
        if (!this.isMounted) {
            // 等待form挂载
            await this.stateHandler.waitForCondition();
        }
        if (!this.form?.meta) {
            throw new Error('<JcxForm /> is not mounted');
        }
        return this.form;
    }

    private updateState() {
        const currentSchema = this.state?.schema ?? [];
        const prevSchema = this.prevState?.schema ?? [];
        // 进行了删除schema操作
        if (currentSchema.length < prevSchema.length) {
            const currentFields = new Set(
                currentSchema.map((item) => item.fieldName),
            );
            const deletedSchema = prevSchema.filter(
                (item) => !currentFields.has(item.fieldName),
            );

            for (const schema of deletedSchema) {
                this.form?.setFieldValue(schema.fieldName, undefined);
            }
        }
    }

    // 如果需要多次更新状态，可以使用 batch 方法
    batchStore(cb: () => void) {
        this.store.batch(cb);
    }

    getLatestSubmissionValues() {
        return this.latestSubmissionValues || {};
    }

    getState() {
        return this.state;
    }

    async getValues() {
        const form = await this.getForm();
        return form.values;
    }

    async isFieldValid(fieldName: string) {
        const form = await this.getForm();
        return form.isFieldValid(fieldName);
    }

    merge(formApi: FormApi) {
        const chain = [this, formApi];
        const proxy = new Proxy(formApi, {
            get(target: any, prop: any) {
                if (prop === 'merge') {
                    return (nextFormApi: FormApi) => {
                        chain.push(nextFormApi);
                        return proxy;
                    };
                }
                if (prop === 'submitAllForm') {
                    return async (needMerge: boolean = true) => {
                        try {
                            const results = await Promise.all(
                                chain.map(async (api) => {
                                    const form = await api.getForm();
                                    const validateResult = await api.validate();
                                    if (!validateResult.valid) {
                                        return;
                                    }
                                    const rawValues = toRaw(form.values || {});
                                    return rawValues;
                                }),
                            );
                            if (needMerge) {
                                const mergedResults = Object.assign({}, ...results);
                                return mergedResults;
                            }
                            return results;
                        } catch (error) {
                            console.error('Validation error:', error);
                        }
                    };
                }
                return target[prop];
            },
        });

        return proxy;
    }

    mount(formActions: FormActions) {
        if (!this.isMounted) {
            Object.assign(this.form, formActions);
            this.stateHandler.setConditionTrue();
            this.setLatestSubmissionValues({...toRaw(this.form.values)});
            this.isMounted = true;
        }
    }

    /**
     * 根据字段名移除表单项
     * @param fields
     */
    async removeSchemaByFields(fields: string[]) {
        const fieldSet = new Set(fields);
        const schema = this.state?.schema ?? [];

        const filterSchema = schema.filter((item) => !fieldSet.has(item.fieldName));

        this.setState({
            schema: filterSchema,
        });
    }

    /**
     * 重置表单
     */
    async resetForm(
        state?: Partial<FormState<GenericObject>> | undefined,
        opts?: Partial<ResetFormOpts>,
    ) {
        const form = await this.getForm();
        return form.resetForm(state, opts);
    }

    async resetValidate() {
        const form = await this.getForm();
        const fields = Object.keys(form.errors.value);
        fields.forEach((field) => {
            form.setFieldError(field, undefined);
        });
    }

    async setFieldValue(field: string, value: any, shouldValidate?: boolean) {
        const form = await this.getForm();
        form.setFieldValue(field, value, shouldValidate);
    }

    setLatestSubmissionValues(values: null | Recordable<any>) {
        this.latestSubmissionValues = {...toRaw(values)};
    }

    setState(
        stateOrFn:
            | ((prev: JcxFormProps) => Partial<JcxFormProps>)
            | Partial<JcxFormProps>,
    ) {
        if (isFunction(stateOrFn)) {
            this.store.setState((prev) => {
                return mergeWithArrayOverride(stateOrFn(prev), prev);
            });
        } else {
            this.store.setState((prev) => mergeWithArrayOverride(stateOrFn, prev));
        }
    }

    /**
     * 设置表单值
     * @param fields record
     * @param filterFields 过滤不在schema中定义的字段 默认为true
     * @param shouldValidate
     */
    async setValues(
        fields: Record<string, any>,
        filterFields: boolean = true,
        shouldValidate: boolean = false,
    ) {
        const form = await this.getForm();
        if (!filterFields) {
            form.setValues(fields, shouldValidate);
            return;
        }

        /**
         * 合并算法有待改进，目前的算法不支持object类型的值。
         * antd的日期时间相关组件的值类型为dayjs对象
         * element-plus的日期时间相关组件的值类型可能为Date对象
         * 以上两种类型需要排除深度合并
         */
        const fieldMergeFn = createMerge((obj, key, value) => {
            if (key in obj) {
                obj[key] =
                    !Array.isArray(obj[key]) &&
                    isObject(obj[key]) &&
                    !isDayjsObject(obj[key]) &&
                    !isDate(obj[key])
                        ? fieldMergeFn(obj[key], value)
                        : value;
            }
            return true;
        });
        const filteredFields = fieldMergeFn(fields, form.values);
        form.setValues(filteredFields, shouldValidate);
    }

    async submitForm(e?: Event) {
        e?.preventDefault();
        e?.stopPropagation();
        const form = await this.getForm();
        await form.submitForm();
        const rawValues = toRaw(form.values || {});
        await this.state?.handleSubmit?.(rawValues);

        return rawValues;
    }

    unmount() {
        this.form?.resetForm?.();
        // this.state = null;
        this.latestSubmissionValues = null;
        this.isMounted = false;
        this.stateHandler.reset();
    }

    updateSchema(schema: Partial<FormSchema>[]) {
        const updated: Partial<FormSchema>[] = [...schema];
        const hasField = updated.every(
            (item) => Reflect.has(item, 'fieldName') && item.fieldName,
        );

        if (!hasField) {
            console.error(
                'All items in the schema array must have a valid `fieldName` property to be updated',
            );
            return;
        }
        const currentSchema = [...(this.state?.schema ?? [])];

        const updatedMap: Record<string, any> = {};

        updated.forEach((item) => {
            if (item.fieldName) {
                updatedMap[item.fieldName] = item;
            }
        });

        currentSchema.forEach((schema, index) => {
            const updatedData = updatedMap[schema.fieldName];
            if (updatedData) {
                currentSchema[index] = mergeWithArrayOverride(
                    updatedData,
                    schema,
                ) as FormSchema;
            }
        });
        this.setState({schema: currentSchema});
    }

    async validate(opts?: Partial<ValidationOptions>) {
        const form = await this.getForm();

        const validateResult = await form.validate(opts);

        if (Object.keys(validateResult?.errors ?? {}).length > 0) {
            console.error('validate error', validateResult?.errors);
        }
        return validateResult;
    }

    async validateAndSubmitForm() {
        const form = await this.getForm();
        const {valid} = await form.validate();
        if (!valid) {
            return;
        }
        return await this.submitForm();
    }

    async validateField(fieldName: string, opts?: Partial<ValidationOptions>) {
        const form = await this.getForm();
        const validateResult = await form.validateField(fieldName, opts);

        if (Object.keys(validateResult?.errors ?? {}).length > 0) {
            console.error('validate error', validateResult?.errors);
        }
        return validateResult;
    }
}
